/*
MIT License

Copyright (c) 2022 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,

https://bmstu.codes/lsx/simodo/loom
*/

#include "simodo/variable/VariableStack.h"
#include "simodo/bormental/DrBormental.h"
#include "simodo/inout/format/fmt.h"

namespace simodo::variable
{
    VariableStack::VariableStack(size_t stack_size)
    {
        _stack.reserve(stack_size);
    }

    void VariableStack::push(Variable v)
    {
        size_t size     = _stack.size();
        size_t capacity = _stack.capacity();

        if (size >= capacity)
            throw bormental::DrBormental("VariableStack::push", 
                            inout::fmt("SBL variable stack overflow occurred (size: %1)")
                            .arg(size));

        _stack.push_back(v);
    }

    Variable VariableStack::pop() 
    {
        if (_stack.size() == 0)
            throw bormental::DrBormental("VariableStack::pop", 
                            inout::fmt("Attempt to pop an element from the empty SBL variable stack"));

        Variable v = _stack.back();

        _stack.pop_back();

        return v;
    }

    void VariableStack::popAmount(size_t n)
    {
        if (n > _stack.size())
            throw bormental::DrBormental("VariableStack::popAmount", 
                            inout::fmt("Attempt to pop too many elements (%1) from the SBL variable stack (size: %2)")
                            .arg(n)
                            .arg(_stack.size()));

        while(n--)
            _stack.pop_back();
    }

    const Variable & VariableStack::top(size_t shift_from_top) const 
    {
        if (shift_from_top >= _stack.size())
            throw bormental::DrBormental("VariableStack::top", 
                            inout::fmt("Attempt to handle invalid offset (%1) in SBL variable stack (size: %2)")
                            .arg(shift_from_top)
                            .arg(_stack.size()));

        return _stack[_stack.size()-shift_from_top-1];
    }

    Variable & VariableStack::top(size_t shift_from_top)
    {
        if (shift_from_top >= _stack.size())
            throw bormental::DrBormental("VariableStack::top", 
                            inout::fmt("Attempt to handle invalid offset (%1) in SBL variable stack (size: %2)")
                            .arg(shift_from_top)
                            .arg(_stack.size()));

        return _stack[_stack.size()-shift_from_top-1];
    }

    const Variable & VariableStack::at(size_t index) const
    {
        if (index > _stack.size())
            throw bormental::DrBormental("VariableStack::at", 
                            inout::fmt("Attempt to handle invalid offset (%1) in SBL variable stack (size: %2)")
                            .arg(index)
                            .arg(_stack.size()));

        return _stack[index]; // -V557
    }

    Variable & VariableStack::at(size_t index)
    {
        if (index > _stack.size())
            throw bormental::DrBormental("VariableStack::at", 
                            inout::fmt("Attempt to handle invalid offset (%1) in SBL variable stack (size: %2)")
                            .arg(index)
                            .arg(_stack.size()));

        return _stack[index]; // -V557
    }

    std::shared_ptr<variable::Object> VariableStack::convertToObject(size_t stack_start_position)
    {
        std::shared_ptr<variable::Object> ret = std::make_shared<variable::Object>();

        for(size_t i=stack_start_position; i < stack().size(); ++i)
            ret->variables().push_back(stack().at(i).copyVariable());

        popAmount(stack().size() - stack_start_position);
        
        return ret;
    }

}